JavaScript
的对象(Object
),本质上是键值对的集合(Hash
结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
const data = {};
const element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
上面代码原意是将一个 DOM
节点作为对象data
的键,但是由于对象只接受字符串作为键名,所以element
被自动转为字符串[object HTMLDivElement]
。
什么是Map
为了解决这个问题,ES6
提供了 Map
数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
也就是说,Object
结构提供了“字符串—值”的对应,Map
结构提供了“值—值”的对应,是一种更完善的 Hash
结构实现。
如果你需要“键值对”的数据结构,Map
比 Object
更合适。
语法
new Map([iterable])
Iterable
可以是一个数组或者其他 iterable
对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]]
。
每个键值对都会添加到新的 Map
。null
会被当做 undefined
。
Object 和 Map 的比较:
Objects
和 Maps
类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps
使用。不过 Maps
和 Objects
有一些重要的区别,在下列情况里使用 Map 会是更好的选择
一个 Object
的键只能是字符串或者 Symbols
,但一个 Map
的键可以是任意值,包括函数、对象、基本类型。
Map
中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时, Map
对象是按插入的顺序返回键值。
你可以通过 size
属性直接获取一个 Map 的键值对个数,而 Object
的键值对个数只能手动计算。
Map
可直接进行迭代,而 Object
的迭代需要先 获取它的键数组,然后再进行迭代。
Object
都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。虽然 ES5
开始可以用 map = Object.create(null)
来创建一个没有原型的对象,但是这种用法不太常见。
Map
在涉及频繁增删键值对的场景下会有些性能优势。
实例
var myMap = new Map();
var keyObj = {},
keyFunc = function () {},
keyString = "a string";
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
myMap.get("a string"); // "和键'a string'关联的值"
// 因为keyString === 'a string'
myMap.get({}); // undefined, 因为keyObj !== {}
myMap.get(function() {}) // undefined, 因为keyFunc !== function () {}
将 NaN 作为 Map 的键
NaN
也可以作为Map
对象的键。虽然 NaN
和任何值甚至和自己都不相等(NaN !== NaN 返回true)
,但下面的例子表明,NaN
作为Map
的键来说是没有区别的
var myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
只有对同一个对象的引用,Map 结构才将其视为同一个键
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
上面代码的set
和get
方法,表面是针对同一个键,但实际上这是两个不同的数组实例,内存地址是不一样的,因此get
方法无法读取该键,返回undefined
。
同样的值的两个实例,在 Map
结构中被视为两个键。